/*
 * @Author: wuqingchun
 * @Date: 2024-10-09 15:32:27
 * @Description: 
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-10-12 15:07:10
 */
#include <cstdio>
#include <cerrno>
#include <cstring>

#include <chrono>
#include <thread>

#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>


#include "netperf_udp.h"



CNetperfUDPClient::CNetperfUDPClient(const NETPERF_PARAM_S& refParam):
    m_refParam(refParam)
{

}

CNetperfUDPClient::~CNetperfUDPClient()
{

}


int32_t CNetperfUDPClient::Init()
{
    m_iSockFd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_UDP);
    if (m_iSockFd == -1)
    {
        printf("socket error, %s\n", strerror(errno));
        return -1;
    }

    return 0;
}

void CNetperfUDPClient::Run()
{
    char acBuf[4096];
    char acRecvBuf[4096];

    NETPERF_MSG_HEAD_S* pstHead = (NETPERF_MSG_HEAD_S*)acBuf;
    NETPERF_MSG_HEAD_S* pstRecvHead = (NETPERF_MSG_HEAD_S*)acRecvBuf;

    fd_set stRFds;
    struct timeval stTV = {};
    const char* pcData = "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
    "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
    "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
    "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
    "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
    "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
    "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
    "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
    "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
    "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";

    int iRet;

    printf("netperf %s://%s:%hu %lu bytes of data\n", m_refParam.enProto == NETPERF_PROTO_TCP ? "tcp":"udp", 
           m_refParam.szHost, m_refParam.usPort, m_refParam.ulPktLen);
    while (true)
    {
        // 发送数据
        pstHead->i64TXTimeStamp = __GetTimeStamp();
        pstHead->ui64SeqNo++;

        memcpy(acBuf + sizeof(NETPERF_MSG_HEAD_S), pcData, m_refParam.ulPktLen - sizeof(NETPERF_MSG_HEAD_S));
        ssize_t lSndLen = sendto(m_iSockFd, acBuf, m_refParam.ulPktLen, 0, (const sockaddr*)&m_refParam.stAddr, 
                                 sizeof(m_refParam.stAddr));
        int64_t i64End = __GetTimeStamp();
        if (lSndLen == m_refParam.ulPktLen)
        {
            m_stStat.ui64TxCnt++;
            __CalculateTxCost(i64End - pstHead->i64TXTimeStamp);
        }
        else
        {
            m_stStat.ui64ErrCnt++;
            continue;
        }

        stTV.tv_sec = m_refParam.uiTimeout/1000000;
        stTV.tv_usec = m_refParam.uiTimeout%1000000;

        FD_ZERO(&stRFds);
        FD_SET(m_iSockFd, &stRFds);

        iRet = select(m_iSockFd + 1, &stRFds, NULL, NULL, &stTV);
        if (iRet == -1)
        {
            perror("select error");
            break;
        }

        if (iRet > 0 && FD_ISSET(m_iSockFd, &stRFds))
        {
            // 接收数据
            int64_t i64RcvStart = __GetTimeStamp();
            ssize_t lRcvLen = recv(m_iSockFd, acRecvBuf, sizeof(acRecvBuf), 0);
            if (lRcvLen > 0)
            {
                int64_t i64RcvEnd = __GetTimeStamp();
                m_stStat.ui64RxCnt++;
                __CalculateRTT(i64RcvEnd - pstRecvHead->i64TXTimeStamp);
                __CalculateRxCost(i64RcvEnd - i64RcvStart);

                printf("%ld bytes from %s:%hu: seqno=%lu time=%.03lf us\n", lRcvLen, m_refParam.szHost, m_refParam.usPort, 
                        pstRecvHead->ui64SeqNo, (i64RcvEnd - pstRecvHead->i64TXTimeStamp)/1000.0);
            }
        }

        usleep(m_refParam.uiInterval);
    }
}


void CNetperfUDPClient::ShowStatistics()
{
    double dAvgRTT = 0.0;
    double dAvgTxCost = 0.0;
    double dAvgRxCost = 0.0;

    if (m_stStat.ui64RxCnt > 0)
    {
        dAvgRTT = m_stStat.ui64TotalRTT/1000.0/m_stStat.ui64RxCnt;
        dAvgTxCost = m_stStat.ui64TotalTxCost/1000.0/m_stStat.ui64TxCnt;
        dAvgRxCost = m_stStat.ui64TotalRxCost/1000.0/m_stStat.ui64RxCnt;
    }

    double d64Loss = m_stStat.ui64TxCnt - m_stStat.ui64RxCnt;
    
    if (m_stStat.ui64TxCnt > 0)
    {
        d64Loss *= 100;
        d64Loss /= m_stStat.ui64TxCnt;
    }

    printf("=== %s:%hu netperf statistics ===\n", m_refParam.szHost, m_refParam.usPort);
    printf("%lu packets transmitted, %lu received, %.02lf%% packet loss\n", 
           m_stStat.ui64TxCnt, m_stStat.ui64RxCnt, d64Loss);
    printf("rtt min/avg/max = %.03lf/%.03lf/%.03lf us\n", m_stStat.i64MinRTT/1000.0, dAvgRTT,
            m_stStat.i64MaxRTT/1000.0);

    printf("send min/avg/max = %.03lf/%.03lf/%.03lf us\n", m_stStat.i64MinTxCost/1000.0, dAvgTxCost,
            m_stStat.i64MaxTxCost/1000.0);
    printf("recv min/avg/max = %.03lf/%.03lf/%.03lf us\n", m_stStat.i64MinRxCost/1000.0, dAvgRxCost,
            m_stStat.i64MaxRxCost/1000.0);
}

void CNetperfUDPClient::__CalculateRTT(int64_t i64RTT)
{
    if (m_stStat.i64MinRTT == 0 || m_stStat.i64MinRTT > i64RTT)
    {
        m_stStat.i64MinRTT = i64RTT;
    }

    if (m_stStat.i64MaxRTT == 0 || m_stStat.i64MaxRTT < i64RTT)
    {
        m_stStat.i64MaxRTT = i64RTT;
    }

    m_stStat.ui64TotalRTT += i64RTT;
}

int64_t CNetperfUDPClient::__GetTimeStamp()
{
    return std::chrono::steady_clock::now().time_since_epoch().count();

    // struct  timeval stTV;
    // (void)gettimeofday(&stTV, NULL);
    // return stTV.tv_sec * 1000000000 + stTV.tv_usec * 1000;
}


void CNetperfUDPClient::__CalculateTxCost(int64_t i64Cost)
{
    if (m_stStat.i64MinTxCost == 0 || m_stStat.i64MinTxCost > i64Cost)
    {
        m_stStat.i64MinTxCost = i64Cost;
    }

    if (m_stStat.i64MaxTxCost == 0 || m_stStat.i64MaxTxCost < i64Cost)
    {
        m_stStat.i64MaxTxCost = i64Cost;
    }

    m_stStat.ui64TotalTxCost += i64Cost;
}

void CNetperfUDPClient::__CalculateRxCost(int64_t i64Cost)
{
    if (m_stStat.i64MinRxCost == 0 || m_stStat.i64MinRxCost > i64Cost)
    {
        m_stStat.i64MinRxCost = i64Cost;
    }

    if (m_stStat.i64MaxRxCost == 0 || m_stStat.i64MaxRxCost < i64Cost)
    {
        m_stStat.i64MaxRxCost = i64Cost;
    }

    m_stStat.ui64TotalRxCost += i64Cost;
}